package com.zzyl.alart.service.impl;

import java.security.Key;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.zzyl.alart.domain.AlertData;
import com.zzyl.alart.service.IAlertDataService;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.device.domain.Device;
import com.zzyl.device.domain.DeviceData;
import com.zzyl.device.mapper.DeviceMapper;
import com.zzyl.device.service.IDeviceService;
import com.zzyl.system.mapper.SysRoleMapper;
import io.netty.util.internal.ObjectUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Cache;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import com.zzyl.alart.mapper.AlertRuleMapper;
import com.zzyl.alart.domain.AlertRule;
import com.zzyl.alart.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 *
 * @author ruoyi
 * @date 2025-10-28
 */
@Slf4j
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private IAlertDataService alertDataService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;

    private long between = 0;

    @Value("${Role.roleRepair}")
    private String roleRepair;

    @Value("${Role.roleAdmin}")
    private String roleAdmin;

    /**
     * 查询报警规则
     *
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     *
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     *
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     *
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     *
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 数据报警校验
     */
    @Override
    public void alertRuleFilter() {
        //获取所有规则列表
        List<AlertRule> list = this.lambdaQuery().eq(AlertRule::getStatus, 1).list();
        if (CollectionUtil.isEmpty(list)) {
            log.info("【数据报警校验】查询到的规则列表为空,无需处理");
            return;
        }

        //获取缓存中的所有JSON格式设备数据
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.LAST_DEVICE_DATA_LIST);
        if (CollectionUtil.isEmpty(values)) {
            log.info("【数据报警校验】查询到的设备最新数据为空,无需处理");
            return;
        }

        //合并所有的设备数据
        List<DeviceData> deviceDataAllList = new ArrayList<>();
        for (Object value : values) {
            List<DeviceData> deviceDataList = JSONUtil.toList((String) value, DeviceData.class);
            deviceDataAllList.addAll(deviceDataList);
        }

        //校验设备数据是否超时
        for (DeviceData d : deviceDataAllList) {
//            int timeout = 3600;
//            LocalDateTime alarmTime = d.getAlarmTime();
//            LocalDateTime now = LocalDateTime.now();
//            long between = Math.abs(LocalDateTimeUtil.between(alarmTime, now, ChronoUnit.SECONDS));
            if (IsTimeOut(d)) {
                log.error("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，数据时间太久，已经超过{}秒，不再处理", d.getDeviceName(), d.getFunctionId(), d.getId(),this.between);
            }

            //  获取所有规则集合
            Set<AlertRule> allAlertRules = getAllAlertRules(d);
            if (CollectionUtil.isEmpty(allAlertRules)) {
                log.error("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，没有查询到相关的报警数据", d.getDeviceName(), d.getFunctionId(), d.getId());
                continue;
            }


            //第一校验：判断是否在规则的生效时间内
            for (AlertRule alertRule : allAlertRules) {
                if (getTimeResult(alertRule)) {
                    log.info("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，规则名：{},设备上报数据时间不在报警规则生效时间内", d.getDeviceName(), d.getFunctionId(), d.getId(), alertRule.getAlertRuleName());
                    continue;
                }

                //持久周期RedisKey
                String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_KEY, d.getIotId(), d.getFunctionId(), alertRule.getAlertRuleName());

                //第二校验：判断设备数据是否达到相应规则的阈值
                if (getInfoResult(alertRule,d)){
                    log.info("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，规则名：{},现在值：{}未达到报警规则阈值：{}", d.getDeviceName(), d.getFunctionId(), d.getId(), alertRule.getAlertRuleName(), d.getDataValue(), Double.valueOf(alertRule.getValue().toString()));
                    redisTemplate.delete(countKey);
                    continue;
                }


                //第三校验：沉默周期校验，是否在不久前已经保存报警数据
                String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_KEY, d.getIotId(), d.getFunctionId(), alertRule.getAlertRuleName());
                Object silentValue = redisTemplate.opsForValue().get(silentKey);
                if (ObjUtil.isNotEmpty(silentValue)) {
                    Long ttl = redisTemplate.getExpire(silentKey);
                    log.error("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，规则名：{},还在沉默周期内，无需处理，沉默周期还剩余{}秒", d.getDeviceName(), d.getFunctionId(), d.getId(), alertRule.getAlertRuleName(), ttl);
                    continue;
                }


                //第四校验：持续周期校验，是否达到最小上报周期
                Object countValue = redisTemplate.opsForValue().increment(countKey);
                Integer duration = alertRule.getDuration();
                if (Integer.valueOf(countValue.toString()) < duration) {
                    log.error("[报警规则校验]设备名称：{}，功能：{}，设备ID：{}，规则名：{},当前为{}次，还未达到{}次", d.getDeviceName(), d.getFunctionId(), d.getId(), alertRule.getAlertRuleName(), countValue.toString(), duration);
                    continue;
                }

                //校验成功，清空持久周期
                redisTemplate.delete(countKey);
                //生成沉默周期，以及存活时间
                redisTemplate.opsForValue().set(silentKey,"666",alertRule.getAlertSilentPeriod(),TimeUnit.MINUTES);

                //判断规则的生效对象，是老人数据规则还是设备数据规则，获取相应执行人的id
                List<Long> userIds = getUserIds(d, alertRule);

                if (CollectionUtil.isEmpty(userIds)){
                    log.error("[报警规则校验]没有查询到任何用户id，设备ID：{}",d.getIotId());
                    continue;
                }

                //生成基础报警数据对象，留白userId空位
                AlertData BasealertData = BeanUtil.toBean(d, AlertData.class);
                BasealertData.setId(null);
                BasealertData.setStatus(0);
                String alertReason = CharSequenceUtil.format("功能{}{}{}+持续周期{}+沉默周期{}", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration(), alertRule.getAlertSilentPeriod());
                BasealertData.setAlertReason(alertReason);
                BasealertData.setType(alertRule.getAlertDataType());
                List<Long> longs = sysRoleMapper.seletUserIdByRoleName(roleAdmin);
                Set<Long> AllUserId = CollUtil.unionDistinct(userIds, longs);

                //遍历合并后的所有执行人id集合，生成报警数据对象并设置userid存入报警数据集合
                List<AlertData> result = getAlertDataList(alertRule, AllUserId, BasealertData);
                //批量插入报警数据
            alertDataService.saveBatch(result);
            log.info("[报警规则校验]成功插入，设备ID：{}",d.getIotId());
            }
        };
    }


    private boolean IsTimeOut(DeviceData d){
        int timeout = 3600;
        LocalDateTime alarmTime = d.getAlarmTime();
        LocalDateTime now = LocalDateTime.now();
        long between = Math.abs(LocalDateTimeUtil.between(alarmTime, now, ChronoUnit.SECONDS));
        this.between = between;
        return between >= timeout;
    }

    /**
     * 根据基础报警数据对象和员工列表生成报警数据集合
     * @param alertRule
     * @param AllUserId
     * @param BasealertData
     * @return
     */
    @NotNull
    private static List<AlertData> getAlertDataList(AlertRule alertRule, Set<Long> AllUserId, AlertData BasealertData) {
        List<AlertData> result = AllUserId.stream().map(id -> {
            AlertData alertData =BeanUtil.toBean(BasealertData,AlertData.class);
            alertData.setUserId(id);
            alertData.setAlertRuleId(alertRule.getId());
            return alertData;
        }).collect(Collectors.toList());
        return result;
    }

    /**
     * 获取员工id列表
     * @param d
     * @param alertRule
     * @return
     */
    @Nullable
    private List<Long> getUserIds(DeviceData d, AlertRule alertRule) {
        List<Long> userIds = null;
        if (alertRule.getAlertDataType() == 0) {
            if (d.getLocationType() == 0) {
                userIds = deviceMapper.getUserIdByRemoteDId(d.getIotId());
            } else if (d.getLocationType() == 1) {
                userIds = deviceMapper.getUserIdByFixedDid(d.getIotId());
            }
        } else if (alertRule.getAlertDataType() == 1) {
            userIds = sysRoleMapper.seletUserIdByRoleName(roleRepair);
        }
        return userIds;
    }

    /**
     * 获取该设备所有规则列表
     * @param d
     * @return
     */
    private Set<AlertRule> getAllAlertRules(DeviceData d) {
        //所有设备规则
        List<AlertRule> alartRuleAllPro = this.lambdaQuery()
                .eq(AlertRule::getProductKey, d.getProductKey())
                .eq(AlertRule::getFunctionId, d.getFunctionId())
                .eq(AlertRule::getIotId, -1)
                .eq(AlertRule::getStatus, 1)
                .list();

        //当前设备规则
        List<AlertRule> alartRuleThisPro = this.lambdaQuery()
                .eq(AlertRule::getProductKey, d.getProductKey())
                .eq(AlertRule::getFunctionId, d.getFunctionId())
                .eq(AlertRule::getIotId, d.getId())
                .eq(AlertRule::getStatus, 1)
                .list();

        //合并所有规则并判空
        Set<AlertRule> allAlertRules = CollUtil.unionDistinct(alartRuleAllPro, alartRuleThisPro);
        return allAlertRules;
    }

    /**
     * 校验设备数据上报时间是否在规则生效时间内
     * @param alertRule
     * @return
     */
    private boolean getTimeResult(AlertRule alertRule){
        List<String> EffectivePeriod = Arrays.asList(alertRule.getAlertEffectivePeriod().split("~"));
        LocalTime startTime = LocalTime.parse(EffectivePeriod.get(0));
        LocalTime endTime = LocalTime.parse(EffectivePeriod.get(1));
        LocalTime nowTime = LocalDateTime.now().toLocalTime();

        return nowTime.isBefore(startTime) || nowTime.isAfter(endTime);
    }

    /**
     * 判断设备数据值是否达到规则阈值
     * @param alertRule
     * @param d
     * @return
     */
    private boolean getInfoResult(AlertRule alertRule,DeviceData d){
        String operator = alertRule.getOperator();
        Double value = Double.valueOf(alertRule.getValue().toString());
        String dataValue = d.getDataValue();
        if (">=".equals(operator)) {
            if (Double.valueOf(dataValue) < value) {
                return true;
            }
        } else if ("<".equals(operator)) {
            if (Double.valueOf(dataValue) >= value) {
                return true;
            }
        }
        return false;
    }
}
